package m.html;

import m.common.model.util.FromQueryList;
import m.common.model.util.QueryPage;
import m.common.model.util.QueryParameter;
import m.html.collect.CollectColumn;
import m.html.element.Element;
import m.html.element.attr.Align;
import m.html.list.ListCount;
import m.html.list.ListSearch;
import m.html.list.SearchType;
import m.html.util.IVueListColumn;
import m.html.util.VueListPage;
import m.system.db.DBManager;
import m.system.db.DataRow;
import m.system.db.DataSet;
import m.system.exception.MException;
import m.system.lang.HtmlBodyContent;
import m.system.util.ArrayUtil;
import m.system.util.JSONMessage;
import m.system.util.ObjectUtil;
import m.system.util.StringUtil;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VueCollectResult extends VueListPage<CollectColumn,VueCollectResult> {
	private final FromQueryList from;
	private List<CollectColumn> cols;


	private boolean originalCount=false;//原始合计 true使用sql自己的合计模式

	/**
	 * 构造方法
	 *  queryData list代表查询数据,count代表记录数和汇总数据  空或其他返回页面
	 */
	public VueCollectResult(String title,FromQueryList from,String dataUrl,String queryData,Map<String,String> params) {
		this.title=title;
		this.subTitle="";
		this.describe="";
		this.from=from;
		this.dataUrl=dataUrl;
		this.queryData=queryData;
		this.params=params;
		this.page=new QueryPage();
		if(null==this.params) this.params= new HashMap<>();
		this.cols= new ArrayList<>();
		this.queryFields= new HashSet<>();
		this.searchMap= new ArrayList<>();
		this.buttons= new ArrayList<>();
		this.bottomButtons=new ArrayList<>();
		this.height=0;
		String[] urlParts=dataUrl.split("/");
		String[] actions=new String[urlParts.length-2];
		System.arraycopy(urlParts,1,actions,0,actions.length);
		this.actionName=ArrayUtil.connection(actions, "/");
	}
	/**
	 * 添加列
	 *  col
	 * 
	 */
	public VueCollectResult addCol(CollectColumn col) {
		this.cols.add(col);
		if(isShowCol(col))
			this.queryFields.addAll(col.getParamFields());
		if(col.getFilter()) {
			if(!col.getDisItems().isEmpty()) {
				col.setFilter(SearchType.HIDDEN_EQ);
			}
			this.addSearch(
				ListSearch.field(col.getTitle(),col.getFieldAs(),col.getFilterType(),col.getDisItems())
					.setDictType(col.getDictType()).setDictTree(col.isDictTree())
					.setNotQuery(true)
			);
		}
		return this;
	}
	public VueCollectResult setOriginalCount(boolean originalCount) {
		this.originalCount = originalCount;
		return this;
	}
	private FromQueryList countFrom=null;
	/**
	 * 设置合计结果
	 */
	public VueCollectResult setCountResult(VueCollectResult result){
		this.countFrom=result.from;
		return this;
	}
	/**
	 * list：列表   count：行数，合计，小计
	 */
	protected JSONMessage getQueryResult(String type) throws Exception {
		JSONMessage result=new JSONMessage();
		result.push("code", 0);
		result.push("asMap", from.getAsMap());
		if(null!=columnsParam) {
			List<CollectColumn> newList = new ArrayList<>();
			for (String[] arr : columnsParam) {
				for (CollectColumn col : cols) {
					if (arr[0].equals(ArrayUtil.isEmpty(col.getGroupTitle()) ? col.getTitle() : col.getGroupTitle()[0])) {
						newList.add(col);
						if(ArrayUtil.isEmpty(col.getGroupTitle())) break;
					}
				}
			}
			this.cols = newList;
		}
		if(type.contains("list")) {
			int sc=this.scol;
			int sl=this.slength;
			List<String> colSb = new ArrayList<>();
			for (int i = 0; i < this.cols.size(); i++) {
				CollectColumn col = this.cols.get(i);
				if (!ArrayUtil.isEmpty(col.getGroupTitle())) {
					String json = toTitleJson(col, i == this.cols.size() - 1);
					if (!StringUtil.isSpace(json)) {
						colSb.add(json);
					}
				} else {
					String json = toTitleJson();
					if (!StringUtil.isSpace(json)) {
						colSb.add(json);
					}
					colSb.add(col.toJsonString());
				}
			}
			String last = toLastJson();
			if (null != last) colSb.add(last);
			List<JSONMessage> cs=resetColumns(colSb);
			for(JSONMessage jm : cs){
				for(IVueListColumn col : cols){
					if(jm.get("title").equals(ArrayUtil.isEmpty(col.getGroupTitle())?col.getTitle():col.getGroupTitle()[0])
							&&col.getFixed()== Align.NONE){
						jm.push("sort","Y");
					}
				}
			}
			result.push("columnsSetting",cs);
			result.push("columns", colSb.stream().map(HtmlBodyContent::new).toArray());
			result.push("scol",sc);
			result.push("slength",sl);
		}
		for (ListSearch ls : this.searchMap) {
			if (ls.isField()) {
				for (CollectColumn col : this.cols) {
					if (col.getFieldAs().equals(ls.getField())) {
						Object[] arr = ls.toCondition(col.getField(), this.params);
						if(col.getField().toLowerCase().matches("^(min|max|count|sum|avg|group_concat|std|variance)\\(.*")){//聚合函数，使用having
							from.addHaving((String) arr[0], ((List<Object>) arr[1]).toArray(new Object[0]));
							if(null!=this.countFrom) this.countFrom.addHaving((String) arr[0], ((List<Object>) arr[1]).toArray(new Object[0]));
						}else{
							from.addCondition((String) arr[0], ((List<Object>) arr[1]).toArray(new Object[0]));
							if(null!=this.countFrom) this.countFrom.addCondition((String) arr[0], ((List<Object>) arr[1]).toArray(new Object[0]));
						}
					}
				}
			}
		}
		Map<String, ListCount> sumMap = new LinkedHashMap<>();
		Map<String,String> sumExpMap=new HashMap<>();
		String subField="";//是否有小计
		String colOrder = "";
		for (CollectColumn col : this.cols) {
			if (!isShowCol(col)) continue;
			if (null!=order && StringUtil.noSpace(order.getName()).equals(col.getFieldAs())) {
				colOrder = col.getField() + " " + order.getOper();
			}
			String k = col.getFieldAs();
			String countFieldExp=col.getCountFieldExpression();
			if (col.getCount() != ListCount.NONE||null!=countFieldExp) {
				sumMap.put(k, col.getCount());
				sumExpMap.put(k,countFieldExp);
			}else {
				if (col.getSubtotal()) subField = k;
			}
		}
		from.addField(this.queryFields.toArray(new String[]{}));
		if(null!=this.countFrom) this.countFrom.addField(this.queryFields.toArray(new String[]{}));
		from.addOrder(colOrder);
		from.setPage(page);
		if(type.contains("list")){
			result.push("data", from.toList());
		}
		if(type.contains("count")) {
			if(this.originalCount){//计数与合计分开，支持合计的保留查询
				result.push("count", from.getCount());//计数
				QueryParameter qp=from.getSumParameter();//原始合计
				DataRow dr = DBManager.queryFirstRow(qp.getSql(), qp.getValueList().toArray(new Object[]{}));
				List<Object[]> ls = new ArrayList<>();
				for (CollectColumn col : this.cols) {
					if (!isShowCol(col)) continue;
					Object n = dr.get(col.getFieldAs());
					if (null != n) {
						ls.add(new Object[]{n, col.getNumberFormat() + col.getDateFormat(), col.getAlign().toString(), col.getFixed().toString(), col.getType(), col.isFalse()});
					} else {
						ls.add(new Object[]{"", "", "", col.getFixed().toString(), col.getType(), col.isFalse()});
					}
				}
				result.push("summary", ls);
			}else {//计数和合计一起查询
				//填充合计行
				if (!sumMap.isEmpty()) {
					QueryParameter qp = null!=countFrom?countFrom.getQueryParameter():from.getQueryParameter();
					StringBuilder sql = new StringBuilder("SELECT count(*) count_num");
					for (String k : sumMap.keySet()) {
						String exp=sumExpMap.get(k);
						if(null!=exp){
							Pattern pattern=Pattern.compile("\\#\\{.+?\\}");
							Matcher matcher=pattern.matcher(exp);
							while(matcher.find()) {
								String str=matcher.group();
								String ns=str.substring(2,str.length()-1);
								exp=exp.replace(str, "a."+ns);
							}
							sql.append(",").append(exp).append(" ");
							sql.append(k);
						}else {
							sql.append(",").append(sumMap.get(k));
							sql.append("(a.").append(k).append(") ");
							sql.append(k);
						}
					}
					sql.append(" FROM (").append(qp.getSubSql()).append(") a");
					DataRow dr = DBManager.queryFirstRow(sql.toString(), qp.getValueList().toArray(new Object[]{}));
					if (null != dr) {
						List<Object[]> ls = new ArrayList<>();
						for (CollectColumn col : this.cols) {
							if (!isShowCol(col)) continue;
							Object n = dr.get(col.getFieldAs());
							if (null != n) {
								ls.add(new Object[]{n, col.getNumberFormat() + col.getDateFormat(), col.getAlign().toString(), col.getFixed().toString(), col.getType(), col.isFalse()});
							} else {
								ls.add(new Object[]{"", "", "", col.getFixed().toString(), col.getType(), col.isFalse()});
							}
						}
						result.push("count", dr.get(Long.class, "count_num").intValue());
						result.push("summary", ls);
					}
				} else {
					result.push("count", from.getCount());
					result.push("summary", new ArrayList<>());
				}
			}
			//填充小计行
			result.push("subfield", subField);
			if(!StringUtil.isSpace(subField)){
				QueryParameter qp = from.getQueryParameter();
				StringBuilder sql = new StringBuilder("SELECT '' nofield");
				StringBuilder groupby =new StringBuilder();
				boolean add=true;
				for (CollectColumn col : this.cols) {
					if (!isShowCol(col)) continue;
					String k = col.getFieldAs();
					if(k.indexOf("tmp_")==0) continue;
					if (col.getCount() != ListCount.NONE && sumMap.containsKey(k)) {
						sql.append(",").append(sumMap.get(k)).append("(a.").append(k).append(") ").append(k);
					}else if(col.getSubtotal()&&subField.equals(k)){
						sql.append(",").append("'' ").append(k);
						add=false;
					}else if(add){
						sql.append(",a.").append(k);
						groupby.append(",a.").append(k);
					}

				}
				sql.append(" FROM (").append(qp.getSubSql()).append(") a ");
				if(groupby.length()>0) sql.append(" GROUP BY ").append(groupby.substring(1));
				DataSet ds = DBManager.executeQuery(sql.toString(), qp.getValueList().toArray(new Object[]{}));
				List<Map<String,Object>> ls=new ArrayList<>();
				for(DataRow dr : ds.rows()){
					Map<String,Object> os = new HashMap<>();
					for (CollectColumn col : this.cols) {
						if (!isShowCol(col)) continue;
						String k=col.getFieldAs();
						os.put(k,dr.get(col.getFieldAs()));
					}
					os.put("type-subtotal",subField);
					ls.add(os);
				}
				result.push("subtotal", ls);
			}else{
				result.push("subtotal", new ArrayList<>());
			}
		}
		return result;
	}
	protected Element getTableElement(){
		return Element.tag("a-table").A("ref", "vueListResultTable")
		.A(":scroll", "{x:width,y:bodyHeight}")
		.A("size","small").A(":columns","columns").A(":data-source","tableData").A("@change","tableChange")
		.A(":pagination","false").A("tableLayout","fixed").A("@resizeColumn","handleResizeColumn")
		.A(":loading","tableLoad");
	}

	@Override
	protected String getVueDataPart() {
		return "";
	}

	@Override
	protected String getVueMethodPart() {
		return "";
	}

	private JSONMessage jtemp=null;
	private String toTitleJson() {
		if(null!=jtemp) {
			String str=jtemp.toJSONString();
			jtemp=null;
			return str;
		}
		return null;
	}
	private String toTitleJson(CollectColumn col,boolean isLast) {
		String[] strs=col.getGroupTitle();
		if(null==jtemp) {
			jtemp=new JSONMessage();
			jtemp.push("title", strs[0]);
			jtemp.push("children", new ArrayList<>());
		}
		if(jtemp.get("title").equals(strs[0])) {
			JSONMessage jt=jtemp;
			for(int i=1;i<strs.length;i++) {
				String s2=strs[i];
				List<Object> chr=(List<Object>)jt.get("children");
				for(Object ch : chr) {
					if(ch instanceof JSONMessage) {
						JSONMessage jm=(JSONMessage)ch;
						if(jm.get("title").equals(s2)) {
							jt=jm;
						}
					}
				}
				if(!jt.get("title").equals(s2)) {
					jt=new JSONMessage();
					jt.push("title", s2);
					jt.push("children", new ArrayList<>());
					chr.add(jt);
				}
			}
			((List<Object>)jt.get("children")).add(new HtmlBodyContent(col.toJsonString()));
			if(isLast) {
				String r=jtemp.toJSONString();
				jtemp=null;
				return r;
			}
		}else {
			String r=toTitleJson();
			if(isLast) {
				return r+","+toTitleJson(col, isLast);
			}else {
				toTitleJson(col,isLast);
				return r;
			}
		}
		return null;
	}
	private String toLastJson() {
		if(null!=jtemp) return jtemp.toJSONString();
		else return null;
	}

	@Override
	protected List<CollectColumn> getListCols() {
		return this.cols;
	}

	protected void fillSubcount(List<?> list, String subfield, List<?> subtotal) throws MException {
		if(StringUtil.isSpace(subfield)|| list.isEmpty()) return;
		List<Map<String,Object>> tableData= (List<Map<String, Object>>) list;
		Map<String,Object> last= tableData.get(0);
		int lastRow=0;
		for(int i=1;i<tableData.size();i++){
			Map<String,Object> row= tableData.get(i);
			boolean b=false;
			for (CollectColumn col : this.cols) {
				String k = col.getFieldAs();
				if (subfield.equals(k)) {
					break;
				}
				if (!ObjectUtil.toString(last.get(k)).equals(ObjectUtil.toString(row.get(k)))) {
					b = true;
					break;
				}
			}
			if(b){
				Map<String,Object> insertRow=getInsertRow(last,subfield,subtotal);
				if(i-1!=lastRow){
					tableData.add(i,insertRow);
					i++;
				}
				last=row;
				lastRow=i;
			}
		}
		if(tableData.size()-1!=lastRow){
			tableData.add(getInsertRow(last,subfield,subtotal));
		}
	}
	private Map<String,Object> getInsertRow(Map<String,Object> lastRow,String subfield,List<?> subtotal) throws MException {
		Map<String,Object> insertRow=new HashMap<>();
		for(Object obj : subtotal){
			Map<String,Object> stRow= (Map<String, Object>) obj;
			boolean f=true;
			for (CollectColumn col : this.cols) {
				String k = col.getFieldAs();
				if (subfield.equals(k)) break;
				if (!ObjectUtil.toString(lastRow.get(k)).equals(ObjectUtil.toString(stRow.get(k)))) {
					f = false;
					break;
				}
			}
			if(f){
				stRow.put(subfield,"小计");
				insertRow=stRow;
				break;
			}
		}
		return insertRow;
	}

}
